package studiodiva.portfolio.client.lib.lang;

import com.google.gwt.core.client.Scheduler;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.Timer;

import java.util.ArrayList;
import java.util.List;

import studiodiva.portfolio.client.lib.collections.GenericIterator;
import studiodiva.portfolio.client.lib.collections.HashMap;
import studiodiva.portfolio.client.lib.collections.Iteration;
import studiodiva.portfolio.client.lib.collections.ListIterator;
import studiodiva.portfolio.client.lib.collections.Map;
import studiodiva.portfolio.client.lib.collections.MapIterator;
import studiodiva.portfolio.client.lib.collections.Mapped;
import studiodiva.portfolio.client.lib.even.Event;

public class Lang {

  public interface Work {
    void start();

    void stop();
  }

  public static final int MINUTE = 60000;

  public static final int SECOND = 1000;

  public static <T> void add(List<T> list, T... items) {
    for (T item : items)
      list.add(item);
  }

  public static void debug(Object... values) {
    Text info = text("Debug: ", getCallingStackInfo()).addSpace();
    createLogMessage(info, values);
    System.out.println(info);
  }

  public static void doLater(int delay_miliseconds, final Runnable runnable) {
    new Timer() {
      @Override
      public void run() {
        runnable.run();
      }
    }.schedule(delay_miliseconds);
  }

  public static void doLater(final Runnable runnable) {
    Scheduler.get().scheduleDeferred(new Command() {
      public void execute() {
        runnable.run();
      }
    });
  }

  public static boolean equal(Object obj1, Object obj2) {
    if (obj1 == null) return obj2 == null;
    return obj1.equals(obj2);
  }

  @SuppressWarnings({"rawtypes", "unchecked"})
  public static <P, A> Event<A> event() {
    return new Event();
  }

  public static boolean is(Object item) {
    return item != null;
  }

  public static boolean is(Object... items) {
    for (Object item : items)
      if (!is(item)) return false;
    return true;
  }

  public static Iteration<Integer> iterate(int count) {
    return new GenericIterator<Integer>(count) {
      @Override
      protected Integer getValue() {
        return getIndex();
      }
    };
  }

  public static <K, V> Iteration<Mapped<K, V>> iterate(java.util.Map<K, V> map) {
    return new MapIterator<K, V>(map);
  }

  public static <T> Iteration<T> iterate(List<T> items) {
    return new ListIterator<T>(items);
  }

  public static <T> Iteration<T> iterate(T... items) {
    return iterate(list(items));
  }

  public static <T> List<T> list() {
    return new ArrayList<T>();
  }

  public static <T> List<T> list(Iterable<T> items) {
    List<T> list = list();
    for (T item : items)
      list.add(item);
    return list;
  }

  public static <T> List<T> list(T... items) {
    List<T> list = list();
    add(list, items);
    return list;
  }

  public static <K, V> Map<K, V> map() {
    return new HashMap<K, V>();
  }

  public static boolean no(Object object) {
    return object == null;
  }

  public static boolean no(Object... objects) {
    if (objects == null) return true;
    for (Object object : objects)
      if (is(object)) return false;
    return true;
  }

  public static Work schedule(final int delay_miliseconds, final Run runnable) {
    final Timer timer = new Timer() {
      @Override
      public void run() {
        runnable.run();
      }
    };
    return new Work() {
      public void start() {
        timer.scheduleRepeating(delay_miliseconds);
      }

      public void stop() {
        timer.cancel();
      }
    };
  }

  public static boolean set(CharSequence value) {
    return value == null ? false : value.length() > 0;
  }

  public static String string(Object... values) {
    Text text = text();
    for (Object object : values)
      text.add(object.toString());
    return text.toString();
  }

  public static Text text(String... values) {
    return new TextImpl(values);
  }

  public static void trace(Object... values) {
    Text info = text("Trace: ");
    createLogMessage(info, values);
    info.addLine().add(getCallingStackTrace());
    System.out.println(info);
  }

  public static RuntimeException unexpected() {
    return new RuntimeException("Unexpected");
  }

  public static RuntimeException unsuported() {
    return new RuntimeException("Unsupported");
  }

  private static void createLogMessage(Text info, Object... values) {
    for (Object object : values)
      info.add(String.valueOf(object)).addSpace();
  }

  private static String createLogString(StackTraceElement element) {
    return string(element.getClassName(), ".", element.getMethodName(),
        "(" + element.getFileName(), ":", element.getLineNumber(), ")").toString();
  }

  private static String getCallingStackInfo() {
    StackTraceElement element = new Throwable().getStackTrace()[2];
    return createLogString(element);
  }

  private static String getCallingStackTrace() {
    Text trace = text();
    for (StackTraceElement element : iterate(new Throwable().getStackTrace()).skip(2))
      trace.add(createLogString(element)).addLine();
    return trace.toString();
  }
}
